[HNOI2008]Cards

2020-03-02
HNOI

题意

求出将长度为$n$的序列染成$A,B,C$个红、蓝、绿色的本质不同方案

$m$个置换给出,$n\leq 60,A,B,C\leq20,m\leq60$

1
输入数据保证任意多次洗牌都可用这m种洗牌法中的一种代替,且对每种洗牌法,都存在一种洗牌法使得能回到原状态。

我也不知道保证这个有什么用

题解

由于颜色数量有限制,不能用Polya

可以用Burnside,对于每个置换求出不动点个数,可以发现一个循环节内颜色一定相同,Dp即可

最后不要忘了幺元,除$m+1$就是答案

调试记录

好像以为$n\leq 20$?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <cstdio>
#include <vector>
#include <cstring>
#define LL long long
using namespace std;
int n, A, B, C, m, p[65];
LL mo, f[65][65][65];
vector <int> sz;
LL calc(){
bool vis[65]; memset(vis, 0, sizeof vis);
sz.clear();
for (int i = 1; i <= n; i++){
if (vis[i]) continue;
int cur = i, t = 0;
while (!vis[cur]){
vis[cur] = 1;
++t;
cur = p[cur];
} sz.push_back(t);
}
memset(f, 0, sizeof f); f[0][0][0] = 1;
for (int i = 0; i < sz.size(); i++){
for (int a = A; a >= 0; a--)
for (int b = B; b >= 0; b--)
for (int c = C; c >= 0; c--){
if (A - a >= sz[i]) f[a + sz[i]][b][c] = (f[a + sz[i]][b][c] + f[a][b][c]) % mo;
if (B - b >= sz[i]) f[a][b + sz[i]][c] = (f[a][b + sz[i]][c] + f[a][b][c]) % mo;
if (C - c >= sz[i]) f[a][b][c + sz[c]] = (f[a][b][c + sz[c]] + f[a][b][c]) % mo;
}
}
return f[A][B][C];
} LL ans = 0;
LL pow(LL x, LL t){
LL res = 1; x %= mo;
while (t > 0){
if (t & 1) res = res * x % mo;
x = x * x % mo;
t >>= 1;
}
return res;
}
int main(){
scanf("%d%d%d%d%lld", &A, &B, &C, &m, &mo); n = A + B + C;
for (int i = 1; i <= m; i++){
for (int j = 1; j <= n; j++)
scanf("%d", p + j);
ans = (ans + calc()) % mo;
}
for (int j = 1; j <= n; j++) p[j] = j; ans = (ans + calc()) % mo;
printf("%lld\n", ans * pow(m + 1, mo - 2) % mo);
return 0;
}